package code;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述:平滑的加权轮询算法
 *  1.三个权重变量
 *      1.weight：配置的权重
 *      2.currentWeight：节点当前权重，会一直变化
 *      3.effectiveWeight：有效权重，初始值为weight，通讯过程中发现异常，则变成-1，之后再选取这个节点，调用成功+1，直到恢复weight。用于健康检查，处理异常节点，降低其权重
 *  2.算法逻辑
 *      1.轮询所有节点，计算当前状态下所有节点的effectiveWeight之和totalWeight
 *      2.currentWeight = currentWeight + effectWeight；选出所有节点中currentWeight中最大的一个节点作为选中节点；
 *      3.选中节点currentWeight = currentWeight - totalWeight
 *  3.
 *
 * @author Madison You
 * @created 21:46
 */
public class SmoothWeightedRoundRobin {

    public static void main(String[] args) {
        Node nodeA = new Node("A", 4);
        Node nodeB = new Node("B", 3);
        Node nodeC = new Node("C", 2);
        SmoothWeightedRoundRobin smoothWeightedRoundRobin = new SmoothWeightedRoundRobin(nodeA,nodeB ,nodeC);
        for (int i = 0; i < 100; i++) {
            Node select = smoothWeightedRoundRobin.select();
            System.out.println(select.getServerName());
        }
    }


    private volatile List<Node> nodeList = new ArrayList<>();
    private ReentrantLock lock = new ReentrantLock();

    public SmoothWeightedRoundRobin(Node... nodes){
        for (Node node : nodes) {
            nodeList.add(node);
        }
    }

    public Node select(){
        try{
            lock.lock();
            return selectInne();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }

    private Node selectInne() {
        Integer totalWeight = 0;
        Node maxNode = null;
        Integer maxWeight = 0;
        for (Node node : nodeList) {
            totalWeight += node.getWeight();
            node.setCurrentWeight(node.getWeight() + node.getCurrentWeight());
            if (maxNode == null || maxWeight < node.getCurrentWeight()) {
                maxNode = node;
                maxWeight = node.getCurrentWeight();
            }
        }
        maxNode.setCurrentWeight(maxNode.getCurrentWeight() - totalWeight);
        return maxNode;
    }


    static class Node{
        private String serverName;
        private Integer weight;
        private Integer currentWeight;

        public Node(String serverName, Integer weight) {
            this.serverName = serverName;
            this.weight = weight;
            this.currentWeight = weight;
        }

        public String getServerName() {
            return serverName;
        }

        public void setServerName(String serverName) {
            this.serverName = serverName;
        }

        public Integer getWeight() {
            return weight;
        }

        public void setWeight(Integer weight) {
            this.weight = weight;
        }

        public Integer getCurrentWeight() {
            return currentWeight;
        }

        public void setCurrentWeight(Integer currentWeight) {
            this.currentWeight = currentWeight;
        }
    }

}
